package cn.t.facade.industry;


import cn.t.constants.CommonConstants;
import cn.t.constants.ParamConstants;
import cn.t.core.mode.PageQueryDTO;
import cn.t.dto.order.OrderQueryRequest;
import cn.t.enums.order.ErrorTypeEnum;
import cn.t.enums.order.OrderTypeEnum;
import cn.t.enums.order.ReplenishmentStatusEnum;
import cn.t.model.device.CargowayDO;
import cn.t.model.device.CargowaySkuDO;
import cn.t.model.device.DeviceDO;
import cn.t.model.order.ExceptionLogDO;
import cn.t.model.order.OrderDO;
import cn.t.model.order.OrderDetailDO;
import cn.t.model.order.OverPickupRecordVO;
import cn.t.model.order.ReplenishmentDO;
import cn.t.model.order.ReplenishmentDetailDO;
import cn.t.model.user.UserQueryDO;
import cn.t.service.device.CargowayService;
import cn.t.service.device.CargowaySkuService;
import cn.t.service.device.DeviceService;
import cn.t.service.order.ExceptionLogService;
import cn.t.service.order.OrderDetailService;
import cn.t.service.order.OrderService;
import cn.t.service.order.OverPickupRecordService;
import cn.t.service.order.ReplenishmentDetailService;
import cn.t.service.order.ReplenishmentService;
import cn.t.service.user.UserQueryService;
import cn.t.utils.BusinessUtil;
import cn.t.utils.DateUtil;
import cn.t.utils.DesensitizationUtil;
import cn.t.vo.industry.IndustryAbnormalOrderVO;
import cn.t.vo.industry.IndustryHotSkuVO;
import cn.t.vo.industry.IndustryOrderVO;
import cn.t.vo.industry.IndustryReplenishmentOrderVO;
import cn.t.vo.industry.IndustryRsSkuVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 领料单服务编排
 */
@Slf4j
@Service
@AllArgsConstructor
public class IndustryOrderFacade {

    private final CargowayService cargowayService;
    private final CargowaySkuService cargowaySkuService;
    private final DeviceService deviceService;
    private final ReplenishmentService replenishmentService;
    private final ReplenishmentDetailService replenishmentDetailService;
    private final OrderService orderService;
    private final OverPickupRecordService overPickupRecordService;
    private final OrderDetailService orderDetailService;
    private final ExceptionLogService exceptionLogService;
    private final UserQueryService userQueryService;

    public List<IndustryOrderVO> getReceivedOrder(Integer count, Long firmId) {
        PageQueryDTO<OrderQueryRequest> request = new PageQueryDTO<>();
        request.setPageNo(ParamConstants.INT_0);
        request.setPageSize(count);
        OrderQueryRequest param = new OrderQueryRequest();
        param.setFirmId(firmId);
        request.setParam(param);
        List<OrderDO> list = orderService.getList(request, OrderTypeEnum.ORDER);

        List<String> orderSns = list.stream().map(OrderDO::getOrderSn).collect(Collectors.toList());
        OrderDetailDO orderDetailDO = new OrderDetailDO();
        orderDetailDO.setOrderSnList(orderSns);
        Map<String, List<OrderDetailDO>> detailMap = orderDetailService.queryListByOrderSn(orderDetailDO).stream().collect(Collectors.groupingBy(OrderDetailDO::getOrderSn));

        return list.stream().map(f -> {
            IndustryOrderVO industryOrderVO = new IndustryOrderVO();
            industryOrderVO.setOrderTime(f.getGmtClose());
            industryOrderVO.setNickname(DesensitizationUtil.generate(1, f.getUserName(), 1, 1));
            industryOrderVO.setOrderSn(f.getOrderSn());
            List<OrderDetailDO> orderDetailDOList = detailMap.get(f.getOrderSn());
            if (!CollectionUtils.isEmpty(orderDetailDOList)) {
                industryOrderVO.setSkuList(orderDetailDOList.stream().map(detail -> new IndustryOrderVO.Sku(detail.getSkuCode(), detail.getSkuName(), detail.getSkuImg(), detail.getQty())).collect(Collectors.toList()));
                industryOrderVO.setTotal(orderDetailDOList.stream().map(OrderDetailDO::getQty).reduce(0, Integer::sum));
                return industryOrderVO;
            } else {
                return null;
            }

        }).filter(Objects::nonNull).collect(Collectors.toList());

    }

    public List<IndustryAbnormalOrderVO> getAbnormalReceivedOrder(Integer count, Long firmId) {
        OrderQueryRequest overPickupRecordVO = new OrderQueryRequest();
        overPickupRecordVO.setFirmId(firmId);
        List<OverPickupRecordVO> list = overPickupRecordService.queryOverPickupRecordLock(overPickupRecordVO, ParamConstants.INT_0, count);

        List<IndustryAbnormalOrderVO> abnormalOrderVOS = new ArrayList<>();
        if (!CollectionUtils.isEmpty(list)) {
            List<IndustryAbnormalOrderVO> overPickData = list.stream()
                    .map(f -> new IndustryAbnormalOrderVO(f.getGmtCreated(), DesensitizationUtil.generate(1, f.getUserName(), 1, 1),
                            "超领", "已锁卡", DesensitizationUtil.generate(3, f.getPhoneNumber(), 4, 4))).collect(Collectors.toList());

            abnormalOrderVOS.addAll(overPickData);
        }

        List<ExceptionLogDO> exceptionLogDOS = exceptionLogService.getExceptionLogs(firmId, count*5, CommonConstants.ONE);
        Map<String, List<ExceptionLogDO>> exceptionLogMap = exceptionLogDOS.stream().collect(Collectors.groupingBy(ExceptionLogDO::getOrderSn));

        List<Long> userIds = exceptionLogDOS.stream().map(ExceptionLogDO::getUserId).collect(Collectors.toList());
        List<UserQueryDO> userQueryDOS = this.userQueryService.findByUserIds(userIds);
        Map<Long, UserQueryDO> userQueryMap = userQueryDOS.stream().collect(Collectors.toMap(UserQueryDO::getId, f -> f));

        exceptionLogMap.forEach((key, val) -> {
            ExceptionLogDO exceptionLog = val.get(0);
            UserQueryDO userQueryDO = userQueryMap.get(exceptionLog.getUserId());
            String phoneNumber = userQueryDO == null ? CommonConstants.BLANK : userQueryDO.getPhoneNumber();

            Set<Integer> exceptionTypes = val.stream().map(ExceptionLogDO::getExceptionType).collect(Collectors.toSet());

            abnormalOrderVOS.add(new IndustryAbnormalOrderVO(exceptionLog.getGmtCreated(), DesensitizationUtil.generate(1, exceptionLog.getUserName(), 1, 1),
                    ErrorTypeEnum.getDescByKey(exceptionTypes), "未锁卡", DesensitizationUtil.generate(3, phoneNumber, 4, 4)));
        });

        // 根据异常时间排序
        abnormalOrderVOS.sort(Comparator.comparing(IndustryAbnormalOrderVO::getOrderTime).reversed());
        // 取top x
        return abnormalOrderVOS.subList(0, Math.min(count, abnormalOrderVOS.size()));
    }

    public IndustryReplenishmentOrderVO getReplenishmentOrder(Integer count, Long firmId) {

        ReplenishmentDO replenishmentQueryParam = new ReplenishmentDO();
        replenishmentQueryParam.setFirmId(firmId);
        List<ReplenishmentDO> replenishmentDOS = this.replenishmentService.findByModel(replenishmentQueryParam, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
        // 没有补货单直接返回
        if (CollectionUtils.isEmpty(replenishmentDOS)) {
            return new IndustryReplenishmentOrderVO(CommonConstants.ZERO, Collections.emptyList());
        }

        List<String> rsSns = replenishmentDOS.stream().map(ReplenishmentDO::getRsSn).collect(Collectors.toList());

        List<ReplenishmentDO> unCompleteOrders = replenishmentDOS.stream()
                .filter(f -> ReplenishmentStatusEnum.PENDING.getCode().equals(f.getRsStatus()) || ReplenishmentStatusEnum.PARTIALLY_COMPLETED.getCode().equals(f.getRsStatus()))
                .collect(Collectors.toList());
        List<ReplenishmentDetailDO> replenishmentDetails = this.replenishmentDetailService.getReplenishmentDetailList(rsSns);
        Map<String, List<ReplenishmentDetailDO>> replenishmentSkuMap = replenishmentDetails.stream().collect(Collectors.groupingBy(ReplenishmentDetailDO::getRsSn));

        List<IndustryReplenishmentOrderVO.Item> items = new ArrayList<>();
        unCompleteOrders.forEach(f -> {

            List<ReplenishmentDetailDO> curReplenishmentDetails = replenishmentSkuMap.get(f.getRsSn());

            if (!CollectionUtils.isEmpty(curReplenishmentDetails)) {

                Set<String> skuCodes = curReplenishmentDetails.stream().map(ReplenishmentDetailDO::getSkuCode).collect(Collectors.toSet());
                int total = curReplenishmentDetails.stream().map(ReplenishmentDetailDO::getRsQty).mapToInt(q -> q).sum();

                String timeToNow;
                long diffTime = DateUtil.diffTime(f.getGmtCreated(), new Date(), TimeUnit.MINUTES);

                if (diffTime >= 1440) {
                    timeToNow = diffTime / 1440 + "天";
                } else if (diffTime >= 60) {
                    timeToNow = diffTime / 60 + "小时";
                } else {
                    timeToNow = diffTime + "分钟";
                }

                items.add(new IndustryReplenishmentOrderVO.Item(f.getGmtModified(), f.getRsSn(), ReplenishmentStatusEnum.getAlias(f.getRsStatus()), skuCodes.size(), total, timeToNow));
            }
        });

        // 根据距今时长排序
        items.sort(Comparator.comparing(IndustryReplenishmentOrderVO.Item::getUpdateTime).reversed());
        // 取top x
        List<IndustryReplenishmentOrderVO.Item> subItems = items.subList(0, Math.min(count, items.size()));

        return new IndustryReplenishmentOrderVO(unCompleteOrders.size(), subItems);
    }

    public IndustryRsSkuVO getReplenishmentSku(Integer count, Long firmId) {
        // 获取设备
        List<DeviceDO> devices = this.deviceService.getDevicesByFirmId(firmId);
        List<Long> deviceIds = devices.stream().map(DeviceDO::getId).collect(Collectors.toList());
        // 设备商品
        List<CargowaySkuDO> cargowaySkuDOS = this.cargowaySkuService.getDeviceSkuList(deviceIds);
        Map<String, List<CargowaySkuDO>> cargowaySkuMap = cargowaySkuDOS.stream().collect(Collectors.groupingBy(CargowaySkuDO::getSkuCode));

        // 获取补货信息
        ReplenishmentDO replenishmentQueryParam = new ReplenishmentDO();
        replenishmentQueryParam.setFirmId(firmId);
        List<ReplenishmentDO> replenishmentDOS = this.replenishmentService.findByModel(replenishmentQueryParam, CommonConstants.ZERO, Integer.MAX_VALUE, null, null);
        List<String> rsSns = replenishmentDOS.stream().map(ReplenishmentDO::getRsSn).collect(Collectors.toList());
        List<ReplenishmentDetailDO> replenishmentDetailDOS = this.replenishmentDetailService.getReplenishmentDetailList(rsSns);
        Map<String, List<ReplenishmentDetailDO>> replenishmentSkuMap = replenishmentDetailDOS.stream().collect(Collectors.groupingBy(ReplenishmentDetailDO::getSkuCode));

        List<IndustryRsSkuVO.Item> items = new ArrayList<>();
        cargowaySkuMap.forEach((key, val) -> {
            CargowaySkuDO cargowaySkuDO = val.get(0);
            List<ReplenishmentDetailDO> curReplenishmentDetails = replenishmentSkuMap.get(key);
            // 实际库存总和
            int totalActQty = val.stream().map(BusinessUtil::calcQty).mapToInt(f -> f).sum();
            // 预警值总和
            int totalAlarmLimit = val.stream().mapToInt(CargowaySkuDO::getAlarmLimit).sum();
            // 在途数量
            int inTransitNum = 0;
            if (!CollectionUtils.isEmpty(curReplenishmentDetails)) {
                inTransitNum = curReplenishmentDetails.stream().mapToInt(ReplenishmentDetailDO::getActualRsQty).sum();
            }
            if (totalAlarmLimit > totalActQty) {
                items.add(new IndustryRsSkuVO.Item(key, cargowaySkuDO.getSkuName(), cargowaySkuDO.getSkuImg(), totalAlarmLimit - totalActQty, inTransitNum));
            }
        });
        // 根据缺货率倒排
        items.sort(Comparator.comparing(IndustryRsSkuVO.Item::getLackNum).reversed());
        // 根据缺货库存算总缺货件数
        int total = items.stream().mapToInt(IndustryRsSkuVO.Item::getLackNum).sum();
        // 取top x
        List<IndustryRsSkuVO.Item> subItems = items.subList(0, Math.min(count, items.size()));

        return new IndustryRsSkuVO(items.size(), total, subItems);
    }

    public List<IndustryHotSkuVO> getHotSku(Integer count, Long firmId) {
        List<IndustryHotSkuVO> industryHotSkuVOS = new ArrayList<>();
        // 查询近60天领用单
        OrderQueryRequest request = new OrderQueryRequest();
        request.setGmtStart(DateUtil.getDateStr(LocalDateTime.now().minusDays(60), DateUtil.DATE_FORMATTER_YMD));
        request.setFirmId(firmId);
        List<OrderDO> orders = this.orderService.getList(request, OrderTypeEnum.ORDER);
        // 只统计有工单的领料单
        orders = orders.stream().filter(f -> StringUtils.hasText(f.getWorkOrderSn())).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(orders)) {
            return Collections.emptyList();
        }

        // 查询领用明细（包含sku）
        List<String> orderSns = orders.stream().map(OrderDO::getOrderSn).collect(Collectors.toList());
        OrderDetailDO orderDetailDO = new OrderDetailDO();
        orderDetailDO.setOrderSnList(orderSns);
        List<OrderDetailDO> orderDetails = this.orderDetailService.queryListByOrderSn(orderDetailDO);

        Map<String, List<OrderDetailDO>> orderDetailMap = orderDetails.stream().collect(Collectors.groupingBy(OrderDetailDO::getSkuCode));

        List<Long> deviceIds = orders.stream().map(OrderDO::getDeviceId).collect(Collectors.toList());
        // 查询货道商品
        List<CargowaySkuDO> cargowaySkuDOS = this.cargowaySkuService.getDeviceSkuList(deviceIds);
        Map<String, List<CargowaySkuDO>> cargowaySkuMap = cargowaySkuDOS.stream().collect(Collectors.groupingBy(CargowaySkuDO::getSkuCode));

        // 查询货道
        List<CargowayDO> cargowayDOS = this.cargowayService.getCargowayList(deviceIds);

        orderDetailMap.forEach((key, val) -> {
            OrderDetailDO orderDetail = val.get(0);
            List<CargowaySkuDO> curCargowaySkuDOS = cargowaySkuMap.get(key);
            if (CollectionUtils.isEmpty(curCargowaySkuDOS)) {
                return;
            }
            Set<Long> curCargowayIds = curCargowaySkuDOS.stream().map(CargowaySkuDO::getCargowayId).collect(Collectors.toSet());

            int skuNum = val.stream().mapToInt(OrderDetailDO::getQty).sum();
            Integer totalSkuInventory = curCargowaySkuDOS.stream().map(CargowaySkuDO::getInventory).mapToInt(f -> f).sum();
            Integer totalCargowayVol = cargowayDOS.stream().filter(f -> curCargowayIds.contains(f.getId())).map(CargowayDO::getCargowayVol).mapToInt(f -> f).sum();
            BigDecimal stockRate = BusinessUtil.getStockRate(totalSkuInventory,totalCargowayVol);
            industryHotSkuVOS.add(new IndustryHotSkuVO(orderDetail.getSkuCode(), orderDetail.getSkuName(), orderDetail.getSkuImg(), skuNum, stockRate + "%"));
        });

        // 根据领用件数倒排
        industryHotSkuVOS.sort(Comparator.comparing(IndustryHotSkuVO::getSkuNum).reversed());
        // 取top x
        return industryHotSkuVOS.subList(0, Math.min(count, industryHotSkuVOS.size()));
    }
}

